package com.colin.library.base;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import com.colin.library.help.LogHelp;

import java.util.ArrayList;
import java.util.List;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

/**
 * Created by Colin on 2017/5/9.
 */

public abstract class BaseRecyclerViewAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    public interface OnLoadMoreDataListener {
        void loadMore();
    }

    private static final int VIEW_TYPE_HEADER = 0x11111111;//header
    private static final int VIEW_TYPE_LIST = 0x2222222;//list
    private static final int VIEW_TYPE_FOOTER = 0x33333333;//footer
    private static final int VIEW_TYPE_EMPTY = 0x44444444;//empty
    private static final int VIEW_TYPE_LOADING = 0x55555555;//loading

    protected Context mContext;
    protected List<T> mData;
    protected LayoutInflater mLayoutInflater;
    private View mItemView;
    //头和尾可能不止一个,用线性布局处理可以加在一起
    private LinearLayout mHeaderLayout;
    private LinearLayout mFooterLayout;

    private LinearLayout mCopyHeaderLayout = null;
    private LinearLayout mCopyFooterLayout = null;
    private View mEmptyView;
    private View mLoadView;
    private View loadMoreFailedView; //View to show if load more failed.

    private boolean mHeadAndEmptyEnable;//是否既显示头又显示空布局
    private boolean mFootAndEmptyEnable;//是否既显示尾又显示空布局
    private boolean mEmptyEnable;
    private boolean mNextLoadEnable = false;
    private boolean mLoadingMoreEnable = false;
    private int layoutResId;
    private int pageSize = -1;
    private SparseArray<Integer> mLayoutResIdArray;//多种布局
    private boolean loadMore = false;//是否支持加载更多

    public BaseRecyclerViewAdapter(List<T> data) {
        this(data, 0);
    }

    public BaseRecyclerViewAdapter(List<T> data, int layoutResId) {
        this.mData = data == null ? new ArrayList<T>() : data;
        this.layoutResId = layoutResId;
    }

    public void setLoadMore(boolean loadMore) {
        this.loadMore = loadMore;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        mContext = parent.getContext();
        this.mLayoutInflater = LayoutInflater.from(mContext);
        BaseViewHolder baseViewHolder = null;
        switch (viewType) {
            case VIEW_TYPE_HEADER:
                baseViewHolder = new BaseViewHolder(mHeaderLayout);
                break;
            case VIEW_TYPE_EMPTY:
                baseViewHolder = new BaseViewHolder(mEmptyView);
                break;
            case VIEW_TYPE_FOOTER:
                baseViewHolder = new BaseViewHolder(mFooterLayout);
                break;
            case VIEW_TYPE_LOADING:
                baseViewHolder = new BaseViewHolder(mLoadView);
                break;
            case VIEW_TYPE_LIST:
                baseViewHolder = onBaseViewHolder(parent, viewType);
                break;
            default:
                baseViewHolder = onBaseViewHolder(parent, viewType);
                break;
        }
        return baseViewHolder;
    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int viewType = holder.getItemViewType();
        switch (viewType) {
            case 0:
                convert((BaseViewHolder) holder, position, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                break;
            case VIEW_TYPE_HEADER:
                break;
            case VIEW_TYPE_EMPTY:
                break;
            case VIEW_TYPE_FOOTER:
                break;
            case VIEW_TYPE_LOADING:
                break;
            case VIEW_TYPE_LIST:
                convert((BaseViewHolder) holder, position, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                break;
            default:
                convert((BaseViewHolder) holder, position, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                break;
        }
    }


    @Override
    public int getItemCount() {
        //根据尾部控件状态 显示foot个数
        int footNumber = isLoadMore() ? 1 : 0;
        int count = mData.size() + footNumber + getHeaderLayoutCount() + getFooterLayoutCount();
        if ((null == mData || mData.size() == 0) && mEmptyView != null) {
            /**
             *  setEmptyView(false) and add emptyView
             */
            if (count == 0 && (!mHeadAndEmptyEnable || !mFootAndEmptyEnable)) {
                count += getEmptyViewCount();
                /**
                 *
                 * {@link #setEmptyView(true, true, View)}
                 */
            } else if (mHeadAndEmptyEnable || mFootAndEmptyEnable) {
                count += getEmptyViewCount();
            }

            if ((mHeadAndEmptyEnable && getHeaderLayoutCount() == 1 && count == 1) || count == 0) {
                mEmptyEnable = true;
                count += getEmptyViewCount();
            }

        }
        return count;
    }


    /**
     * Get the type of View that will be created by {@link #getItemView(int, ViewGroup)} for the specified item.
     *
     * @param position The position of the item within the adapter's data set whose view type we
     *                 want.
     * @return An integer representing the type of View. Two views should share the same type if one
     * can be converted to the other in {@link #getItemView(int, ViewGroup)}. Note: Integers must be in the
     * range 0 to {@link #getItemCount()} - 1.
     */
    @Override
    public int getItemViewType(int position) {
        //position 0 开始
        //count size 加数量

        //if set headView and position =0
        if (null != mHeaderLayout && position == 0) {
            return VIEW_TYPE_HEADER;
        }
        //if user has no data and add emptyView and position <2{(headview +emptyView)} 有1 无0
        if (null != mEmptyView && (null == mData || mData.size() == 0) && position == getHeaderLayoutCount()) {
            return VIEW_TYPE_EMPTY;
        }
        //加载中
        if (null != mFooterLayout && position == getItemCount() && isLoadMore()) {
            return VIEW_TYPE_LOADING;
        }
        //加载完成
        if (null != mFooterLayout && position == getItemCount() && !isLoadMore()) {
            return VIEW_TYPE_FOOTER;
        }
        return VIEW_TYPE_LIST;
    }

    /**
     * 加载List布局
     *
     * @param parent
     * @param viewType
     * @return
     */
    private BaseViewHolder onBaseViewHolder(ViewGroup parent, int viewType) {
        if (mItemView == null) {
            return new BaseViewHolder(getItemView(layoutResId, parent));
        }
        return new BaseViewHolder(mItemView);
    }

    /**
     * @param layoutResId ID for an XML layout resource to load
     * @param parent      Optional view to be the parent of the generated hierarchy or else simply an object that
     *                    provides a set of LayoutParams values for root of the returned
     *                    hierarchy
     * @return view will be return
     */
    protected View getItemView(int layoutResId, ViewGroup parent) {
        return mLayoutInflater.inflate(layoutResId, parent, false);
    }

    /**
     * 得到空布局个数
     *
     * @return
     */
    private int getEmptyViewCount() {
        return mEmptyView == null ? 0 : 1;
    }

    /**
     * 得到头的个数
     *
     * @return
     */
    private int getHeaderLayoutCount() {
        return null == mHeaderLayout ? 0 : 1;
    }

    /**
     * 得到尾的个数
     */
    public int getFooterLayoutCount() {
        return mFooterLayout == null ? 0 : 1;
    }

    /**
     * 是否加载更多状态
     *
     * @return
     */
    private boolean isLoadMore() {
        return mNextLoadEnable && pageSize != -1 && mOnLoadMoreDataListener != null && mData.size() >= pageSize;
//        return loadMore && mOnLoadMoreDataListener != null && mData.size() >= 0;
    }

    /**
     * Append header to the rear of the mHeaderLayout.
     *
     * @param header
     */
    public void addHeaderView(View header) {
        addHeaderView(header, -1);
    }

    /**
     * Add header view to mHeaderLayout and set header view position in mHeaderLayout.
     * When index = -1 or index >= child count in mHeaderLayout,
     * the effect of this method is the same as that of {@link #addHeaderView(View)}.
     *
     * @param header
     * @param index  the position in mHeaderLayout of this header.
     *               When index = -1 or index >= child count in mHeaderLayout,
     *               the effect of this method is the same as that of {@link #addHeaderView(View)}.
     */
    public void addHeaderView(View header, int index) {
        if (null == header) {
            LogHelp.e("头布局不能为空");
            return;
        }
        mHeaderLayout = getLinearLayout(header, true);
        if (null == mHeaderLayout) {
            LogHelp.e("没有得到头布局");
            return;
        }
        index = index >= mHeaderLayout.getChildCount() ? -1 : index;
        mHeaderLayout.addView(header, index);
        this.notifyDataSetChanged();
    }

    /**
     * Append footer to the rear of the mFooterLayout.
     *
     * @param footer
     */
    public void addFooterView(View footer) {
        addFooterView(footer, -1);
    }

    /**
     * Add footer view to mFooterLayout and set footer view position in mFooterLayout.
     * When index = -1 or index >= child count in mFooterLayout,
     * the effect of this method is the same as that of {@link #addFooterView(View)}.
     *
     * @param footer
     * @param index  the position in mFooterLayout of this footer.
     *               When index = -1 or index >= child count in mFooterLayout,
     *               the effect of this method is the same as that of {@link #addFooterView(View)}.
     */
    public void addFooterView(View footer, int index) {
        if (null == footer) {
            LogHelp.e("尾布局不能为空");
            return;
        }
        mFooterLayout = getLinearLayout(footer, false);
        if (null == mFooterLayout) {
            LogHelp.e("没有得到尾布局");
        }
        mNextLoadEnable = false;
        index = index >= mFooterLayout.getChildCount() ? -1 : index;
        mFooterLayout.addView(footer, index);
        this.notifyItemChanged(getItemCount());
    }

    /**
     * 构建头布局和尾布局
     *
     * @param view
     * @param head
     * @return
     */
    private LinearLayout getLinearLayout(View view, boolean head) {
        if (null == view) {
            return null;
        }

        if (head && null == mCopyHeaderLayout) {
            mCopyHeaderLayout = new LinearLayout(view.getContext());
            mCopyHeaderLayout.setOrientation(LinearLayout.VERTICAL);
            mCopyHeaderLayout.setLayoutParams(new RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
        } else if (!head && null == mCopyFooterLayout) {
            mCopyFooterLayout = new LinearLayout(view.getContext());
            mCopyFooterLayout.setOrientation(LinearLayout.VERTICAL);
            mCopyFooterLayout.setLayoutParams(new RecyclerView.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
        }
        if (head) {
            return mCopyHeaderLayout;
        } else {
            return mCopyFooterLayout;
        }
    }


    /**
     * remove header view from mHeaderLayout.
     * When the child count of mHeaderLayout is 0, mHeaderLayout will be set to null.
     *
     * @param header
     */
    public void removeHeaderView(View header) {
        if (mHeaderLayout == null) {
            return;
        }
        mHeaderLayout.removeView(header);
        if (mHeaderLayout.getChildCount() == 0) {
            mHeaderLayout = null;
            mCopyHeaderLayout = null;
        }
        this.notifyDataSetChanged();
    }

    /**
     * remove footer view from mFooterLayout,
     * When the child count of mFooterLayout is 0, mFooterLayout will be set to null.
     *
     * @param footer
     */
    public void removeFooterView(View footer) {
        if (mFooterLayout == null) {
            return;
        }
        mFooterLayout.removeView(footer);
        if (mFooterLayout.getChildCount() == 0) {
            mFooterLayout = null;
            mCopyFooterLayout = null;
        }
        this.notifyDataSetChanged();
    }

    /**
     * remove all header view from mHeaderLayout and set null to mHeaderLayout
     */
    public void removeAllHeaderView() {
        if (mHeaderLayout == null) {
            return;
        }
        mHeaderLayout.removeAllViews();
        mHeaderLayout = null;
        mCopyHeaderLayout = null;
    }

    /**
     * remove all footer view from mFooterLayout and set null to mFooterLayout
     */
    public void removeAllFooterView() {
        if (mFooterLayout == null) {
            return;
        }
        mFooterLayout.removeAllViews();
        mFooterLayout = null;
        mCopyFooterLayout = null;
    }

    /**
     * Set the view to show when load more failed.
     */
    public void setLoadMoreFailedView(View view) {
        loadMoreFailedView = view;
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                removeFooterView(loadMoreFailedView);
                openLoadMore(pageSize);
            }
        });
    }

    /**
     * Call this method when load more failed.
     */
    public void showLoadMoreFailedView() {
        loadComplete();
        if (loadMoreFailedView == null) {
            loadMoreFailedView = mLayoutInflater.inflate(R.layout.def_load_more_failed, null);
            loadMoreFailedView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    removeFooterView(loadMoreFailedView);
                    openLoadMore(pageSize);
                }
            });
        }
        addFooterView(loadMoreFailedView);
    }

    public void loadComplete() {
        mNextLoadEnable = false;
        mLoadingMoreEnable = false;
        this.notifyItemChanged(getItemCount());
    }

    /**
     * when adapter's data size than pageSize and enable is true,the loading more function is enable,or disable
     *
     * @param pageSize
     */
    public void openLoadMore(int pageSize) {
        this.pageSize = pageSize;
        mNextLoadEnable = true;
    }

    private OnLoadMoreDataListener mOnLoadMoreDataListener;

    public void setOnLoadMoreDataListener(OnLoadMoreDataListener onLoadMoreDataListener) {
        mOnLoadMoreDataListener = onLoadMoreDataListener;
    }

    /**
     * Implement this method and use the helper to adapt the view to the given item.
     *
     * @param helper   A fully initialized helper.
     * @param position
     * @param item     The item that needs to be displayed.
     */
    protected abstract void convert(BaseViewHolder helper, int position, T item);
}
